package admin

import (
	"errors"
	"exercise/application/yiran/app/libs"
	"exercise/application/yiran/app/model"
	"exercise/application/yiran/app/request/adminRequest"
	"fmt"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type allotService struct {
	ctx *gin.Context
}

// PreAllocate 预分配学生
func (a *allotService) PreAllocate(request *adminRequest.PreAllocateRequest) (err error) {

	schoolId, _ := a.ctx.Get("schoolId")
	time := new(model.ExamTime)
	err = model.NewDb("").FindOne(time, "id = ? ", []interface{}{request.TimeId}, "*", []string{})
	if err != nil {
		return fmt.Errorf("考次不存在：%w", err)
	}

	//获取符合要求的学生
	studentList := make([]model.ExamStudent18817, 0)
	sql, where := NewExamTimeService().GetWhereByStuRange(time, request.Year)

	orderOrGroup := new(model.OrderOrGroup)
	orderOrGroup.Order = "id desc"
	paging := new(model.Paging)

	err = model.NewDb("").FindAll(&studentList, sql, where, *orderOrGroup, *paging, []string{})
	if err != nil {
		return fmt.Errorf("暂无匹配的学生信息")
	}

	//符合要求的学生写入预分配表
	allData := make([]map[string]interface{}, 0)
	preAllocate := new(model.PreAllocate)

	for _, stu := range studentList {
		insertData := make(map[string]interface{}, 0)
		insertData["school_id"] = schoolId
		insertData["year"] = request.Year
		insertData["time_id"] = request.TimeId
		insertData["status"] = 1
		insertData["stu_id"] = stu.ID
		insertData["bkzy1_zh"] = stu.Bkzy1Zh
		insertData["xm"] = stu.Xm
		insertData["cate_id"] = stu.CateID
		insertData["create_time"] = libs.CurrentTime()
		allData = append(allData, insertData)
		if len(allData) == 200 {
			err = model.NewDb("").BatchInsertRecord(preAllocate, allData)
			if err != nil {
				return err
			}
			allData = []map[string]interface{}{}
		}
	}

	if len(allData) > 0 {
		model.NewDb("").BatchInsertRecord(preAllocate, allData)
		if err != nil {
			return err
		}
	}

	//初始化人员数量
	if len(studentList) > 0 {
		model.NewDb("").UpdateRecord(&model.ExamTime{}, "id = ?", []interface{}{request.TimeId}, map[string]interface{}{
			"stu_number": len(studentList),
		})
	}
	return err
}

// ClearAllocate 清除已分配的学生
func (a *allotService) ClearAllocate(request *adminRequest.ClearAllocateRequest) (err error) {

	time := new(model.ExamTime)
	//获取考次信息
	err = model.NewDb("").FindOne(time, "id = ? ", []interface{}{request.TimeId}, "*", []string{})
	if err != nil {
		return fmt.Errorf("考次不存在：%w", err)
	}
	query := model.NewDb("").TXQuery()

	//清除已分配考场的学生
	studentExam := new(model.StudentExam)
	err = query.Where("time_id = ?", []interface{}{request.TimeId}).Delete(studentExam).Error
	if err != nil {
		query.Rollback()
		return errors.New("清除学生考场信息失败")
	}
	preAllocate := new(model.PreAllocate)
	//pid不存在的删除已分配的学生,且删除预表中的学生
	if time.Pid == 0 {
		//清除预分配数据
		err = query.Where("time_id = ?", []interface{}{request.TimeId}).Delete(preAllocate).Error
		if err != nil {
			query.Rollback()
			return errors.New("清除预分配学生失败")
		}
	} else {
		//清除预分配数据
		err = query.Model(preAllocate).Where("time_id = ?", []interface{}{request.TimeId}).Updates(map[string]interface{}{
			"status":      1,
			"fp_time":     0,
			"all_time_id": 0,
		}).Error
		if err != nil {
			query.Rollback()
			return errors.New("清除预分配学生失败")
		}
	}

	//pid存在的删已分配的学生,且修改预表中的状态

	query.Commit()
	return nil
}

// AutoExamRoom 自动分配
func (a *allotService) AutoExamRoom(request *adminRequest.AutoExamRoomRequest) (res map[string]interface{}, err error) {
	schoolId, _ := a.ctx.Get("schoolId")
	time := new(model.ExamTime)
	//获取考次信息
	err = model.NewDb("").FindOne(time, "id = ? ", []interface{}{request.TimeId}, "*", []string{})
	if err != nil {
		return nil, fmt.Errorf("考次不存在：%w", err)
	}

	//成功预约多少人,共多少人
	preTime := libs.If(time.Pid == 0, time.Id, time.Pid)

	query := model.NewDb("").Query()
	sql, where := NewExamTimeService().GetWhereByJzwRoom(time, schoolId)
	//获取已分配的房间信息
	roomIds, _ := a.fpRoomId(request.TimeId)
	if len(roomIds) > 0 {
		sql += " AND id NOT IN ?"
		where = append(where, roomIds)
	}

	roomList := make([]model.RoomDetail, 0)
	err = query.Where(sql, where...).Order("id DESC").Find(&roomList).Error

	if err != nil {
		return nil, fmt.Errorf("房间不存在：%w", err)
	}

	if time.IsApp == 1 {
		err = a.unAppExam(roomList, *time, request)
	} else {
		err = a.appExam(roomList, *time, request)
	}

	if err != nil {
		return nil, err
	}

	//总数
	var total, unTotal int64
	_ = query.Model(&model.PreAllocate{}).Where("time_id = ? AND status = ?", []interface{}{preTime, 2}...).Count(&total).Error
	_ = query.Model(&model.PreAllocate{}).Where("time_id = ? AND status = ?", []interface{}{preTime, 1}...).Count(&unTotal).Error
	return map[string]interface{}{
		"total":   total,
		"unTotal": unTotal,
	}, nil
}

// ExamList 考场列表
func (a *allotService) ExamList(request *adminRequest.ExamListRequest) (res map[string]interface{}, err error) {
	time := new(model.ExamTime)
	//获取考次信息
	err = model.NewDb("").FindOne(time, "id = ? ", []interface{}{request.TimeId}, "*", []string{})
	if err != nil {
		return nil, fmt.Errorf("考次不存在：%w", err)
	}
	query := model.NewDb("").Query()

	/*list := make([]map[string]interface{}, 0)
	studentExam := new(model.StudentExam)
	err = query.Model(studentExam).Select("room_id,count(id) as total").Where("time_id = ?", request.TimeId).Group("room_id").Find(&list).Error
	//循环list分别查询房间信息和房间下的学生信息*/

	list := make([]model.StudentExamRoom, 0)
	err = query.Where("time_id = ?", request.TimeId).Group("room_id").Preload("Room").Preload("StuList", func(db *gorm.DB) *gorm.DB {
		return db.Order("seat_number DESC")
	}).Find(&list).Error

	if err != nil {
		return nil, fmt.Errorf("暂无考场信息：%w", err)
	}

	return map[string]interface{}{"list": list}, nil
}

// AutoFillsEmptyRoom 自动补齐空考场
func (a allotService) AutoFillsEmptyRoom(request *adminRequest.AutoFillsEmptyRoomRequest) (res interface{}, err error) {
	schoolId, _ := a.ctx.Get("schoolId")
	time := new(model.ExamTime)
	//获取考次信息
	err = model.NewDb("").FindOne(time, "id = ? ", []interface{}{request.TimeId}, "*", []string{})
	if err != nil {
		return nil, fmt.Errorf("考次不存在：%w", err)
	}

	//获取考场的全部房间
	sql, where := NewExamTimeService().GetWhereByJzwRoom(time, schoolId)

	query := model.NewDb("").TXQuery()
	roomList := make([]model.RoomList, 0)
	err = query.Where(sql, where...).Order("id DESC").Preload("ExamStudentList").Find(&roomList).Error

	if err != nil {
		return nil, errors.New("获取考场异常")
	}

	if len(roomList) == 0 {
		return nil, errors.New("该考次暂未分配")
	}

	for _, room := range roomList {
		if len(room.ExamStudentList) == room.SeatTotal {
			continue
		}
		//从预分配表中获取人员补齐房间，不考虑删除人员的情况,座位号都是递增的
		limit := room.SeatTotal - len(room.ExamStudentList)
		stuList := make([]model.PreAllocate, 0)
		err = query.Where("time_id = ? and status = ?", []interface{}{request.TimeId, 1}...).Order("id DESC").Limit(limit).Find(&stuList).Error
		if err != nil {
			return nil, errors.New("暂无待分配人员")
		}

		//获取该房间的最大座位号
		studentExam := new(model.StudentExam)
		var maxSeatNumber int64
		query.Model(studentExam).Select("max(seat_number)").Where("time_id = ? and room_id = ?", []interface{}{request.TimeId, room.Id}...).First(&maxSeatNumber)

		insertStudentExamAllData := make([]map[string]interface{}, 0)
		preIds := make([]interface{}, 0)
		for i := 0; i < len(stuList); i++ {
			step := i + 1
			insertStudentExamData := make(map[string]interface{}, 0)
			insertStudentExamData["school_id"] = schoolId
			insertStudentExamData["stu_id"] = stuList[i].StuId
			insertStudentExamData["stu_name"] = stuList[i].Xm
			insertStudentExamData["room_id"] = room.Id
			insertStudentExamData["time_id"] = time.Id
			insertStudentExamData["exam_type"] = time.ExamType
			insertStudentExamData["year"] = request.Year
			insertStudentExamData["seat_number"] = maxSeatNumber + int64(step)
			insertStudentExamAllData = append(insertStudentExamAllData, insertStudentExamData)

			if len(insertStudentExamAllData) == limit {
				err = query.Model(studentExam).Create(insertStudentExamAllData).Error
				if err != nil {
					query.Rollback()
					return nil, errors.New("分配学生考试异常：" + err.Error())
				}
			}
		}
		//修改学生分配状态
		if len(preIds) > 0 {
			err = query.Model(&model.PreAllocate{}).Where("id IN ?", []interface{}{preIds}...).Updates(map[string]interface{}{
				"fp_time":     libs.CurrentTime(),
				"status":      2,
				"all_time_id": time.Id,
			}).Error

			if err != nil {
				query.Rollback()
				return nil, errors.New("修改学生分配状态异常：" + err.Error())
			}
		}
	}

	query.Commit()

	return roomList, nil
}

// fpRoomId 获取已分配过的房间
func (a *allotService) fpRoomId(timeId int64) ([]int64, error) {
	query := model.NewDb("").Query()
	studentExam := make([]model.StudentExam, 0)
	err := query.Where("time_id = ?", timeId).Group("room_id").Find(&studentExam).Error
	if err != nil {
		return nil, err
	}
	list := make([]int64, 0)
	for _, stu := range studentExam {
		list = append(list, stu.RoomID)
	}
	return list, nil
}

// unAppExam 非预约考场
func (a *allotService) unAppExam(roomList []model.RoomDetail, time model.ExamTime, request *adminRequest.AutoExamRoomRequest) (err error) {
	if len(roomList) == 0 {
		return errors.New("暂无可分配的房间")
	}
	schoolId, _ := a.ctx.Get("schoolId")
	//开启事务
	query := model.NewDb("").TXQuery()

	for _, room := range roomList {
		//获取房间座位数量的学生
		preAllocate := make([]model.PreAllocate, 0)
		err = query.Where("time_id = ? and status = ? ", []interface{}{request.TimeId, 1}...).Order("id DESC").Limit(room.SeatTotal).Find(&preAllocate).Error
		if err != nil {
			query.Rollback()
			return errors.New("未匹配到对应的学生数据")
		}
		if len(preAllocate) == 0 {
			return errors.New("暂无匹配的学生,请先预分配学生")
		}

		insertStudentExamAllData := make([]map[string]interface{}, 0)
		preIds := make([]interface{}, 0)
		for i := 0; i < len(preAllocate); i++ {
			insertStudentExamData := make(map[string]interface{}, 0)
			insertStudentExamData["school_id"] = schoolId
			insertStudentExamData["stu_id"] = preAllocate[i].StuId
			insertStudentExamData["stu_name"] = preAllocate[i].Xm
			insertStudentExamData["room_id"] = room.Id
			insertStudentExamData["time_id"] = time.Id
			insertStudentExamData["exam_type"] = time.ExamType
			insertStudentExamData["year"] = request.Year
			insertStudentExamData["seat_number"] = i + 1
			insertStudentExamAllData = append(insertStudentExamAllData, insertStudentExamData)
			//更新预表学生分配状态
			preIds = append(preIds, preAllocate[i].Id)
		}
		//学生考试表写入数据
		if len(insertStudentExamAllData) > 0 {
			studentExam := new(model.StudentExam)
			err = query.Model(studentExam).Create(insertStudentExamAllData).Error
			if err != nil {
				query.Rollback()
				return errors.New("分配学生考试异常：" + err.Error())
			}

		}
		//修改学生分配状态
		if len(preIds) > 0 {
			err = query.Model(&model.PreAllocate{}).Where("id IN ?", []interface{}{preIds}...).Updates(map[string]interface{}{
				"fp_time":     libs.CurrentTime(),
				"status":      2,
				"all_time_id": time.Id,
			}).Error

			if err != nil {
				query.Rollback()
				return errors.New("修改学生分配状态异常：" + err.Error())
			}
		}
	}
	//事务提交
	query.Commit()
	return nil
}

// appExam 预约考场
func (a *allotService) appExam(roomList []model.RoomDetail, time model.ExamTime, request *adminRequest.AutoExamRoomRequest) (err error) {

	if len(roomList) == 0 {
		return errors.New("暂无可分配的房间")
	}

	schoolId, _ := a.ctx.Get("schoolId")
	//开启事务
	query := model.NewDb("").TXQuery()

	for _, room := range roomList {
		//房间里分配的是空位子
		insertStudentExamAllData := make([]map[string]interface{}, 0)
		for i := 0; i < room.SeatTotal; i++ {
			insertStudentExamData := make(map[string]interface{}, 0)
			insertStudentExamData["school_id"] = schoolId
			insertStudentExamData["stu_id"] = 0
			insertStudentExamData["stu_name"] = ""
			insertStudentExamData["room_id"] = room.Id
			insertStudentExamData["time_id"] = time.Id
			insertStudentExamData["exam_type"] = time.ExamType
			insertStudentExamData["year"] = request.Year
			insertStudentExamData["seat_number"] = i + 1
			insertStudentExamAllData = append(insertStudentExamAllData, insertStudentExamData)
		}
		//学生考试表写入数据
		if len(insertStudentExamAllData) > 0 {
			studentExam := new(model.StudentExam)
			err = query.Model(studentExam).Create(insertStudentExamAllData).Error
			if err != nil {
				query.Rollback()
				return errors.New("分配学生考试异常：" + err.Error())
			}
		}
	}
	//事务提交
	query.Commit()
	return nil
}

func NewAllotService(c *gin.Context) *allotService {
	return &allotService{ctx: c}
}
